/* 观察者模式（使用trait实现） */

pub trait Observer {
    fn update(&self, product: &str);
}

pub trait Subject<'a, T: Observer> {
    fn attach(&mut self, observer: &'a T);

    fn detach(&mut self, observer: &'a T);

    fn notify_observers(&self, product: &str);
}

pub struct Store<'a, T: Observer> {
    observers: Vec<&'a T>
}

impl <'a, T: Observer + PartialEq> Store<'a, T> {
    fn new() -> Store<'a, T> {
        Store {
            observers: Vec::new()
        }
    }
}

impl <'a, T: Observer + PartialEq> Subject<'a, T> for Store<'a, T> {
    fn attach(&mut self, observer: &'a T) {
        self.observers.push(observer);
    }

    fn detach(&mut self, observer: &'a T) {
        if let Some(idx) = self.observers.iter().position(|x| *x == observer) {
            self.observers.remove(idx);
        }
    }

    fn notify_observers(&self, product: &str) {
        for observer in self.observers.iter() {
            observer.update(product);
        }
    }
}

#[derive(PartialEq)]
pub struct Customer {
    name: String
}

impl Observer for Customer {
    fn update(&self, product: &str) {
        println!("客户[{}] 收到了新商品[{}]的通知", self.name, product);
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_observer_generic_1() {
        let customer = Customer {name: String::from("周女士")};
        let customer2 = Customer {name: String::from("王老板")};

        let mut store = Store::new();
        store.attach(&customer);
        store.attach(&customer2);

        store.notify_observers(&String::from("苹果"));

        store.detach(&customer2);

        store.notify_observers(&String::from("凤梨"));
    }
}